package com.hitqz.robot.driver.netty.strategy;

import cn.hutool.core.util.HexUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.google.common.util.concurrent.RateLimiter;
import com.hitqz.robot.api.business.event.TrackRemoteModeTriggerEvent;
import com.hitqz.robot.api.business.event.TrackUltrasonicTriggerEvent;
import com.hitqz.robot.api.common.dto.warning.AddWarningDto;
import com.hitqz.robot.driver.dto.CalibrationPointDto;
import com.hitqz.robot.driver.dto.LocateStatusDto;
import com.hitqz.robot.driver.netty.protocol.message.TrackMessage;
import com.hitqz.robot.driver.util.TrackCacheUtil;
import com.hitqz.robot.driver.util.TrackToolkit;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Objects;
import java.util.Optional;

/**
 * 消息解析，只做信息保存或者发布事件，不做业务逻辑处理
 */
@Slf4j
public class Track64Strategy implements TrackMessageHandlerStrategy {

    private static final RateLimiter logLimiter = RateLimiter.create(1);
    private static final RateLimiter locationLogLimiter = RateLimiter.create(1);
    private static int lastNetStatus = 0;
    private static int lastLeftDriverErrorCode = 0;
    private static int lastRightDriverErrorCode = 0;
    private static int lastRunningErrorCode1 = 0;
    private static byte lastUltrasonicTriggerStatus = (byte) 0;
    private static short lastChargerError = 0;

    @Override
    public void handle(ChannelHandlerContext ctx, TrackMessage message) {
        // 获取消息中的数据
        byte[] data = message.getData();
        int mainCmd = data[0]&0xFF;//转换为无符号
        int subCmd = data[1]&0xFF;//转换为无符号
        // 分配 ByteBuf 并将数据写入
        ByteBuf buffer = ctx.alloc().buffer(data.length);
        buffer.writeBytes(data);
        try {
            mainCmd = buffer.readByte()&0xFF;
            subCmd = buffer.readByte()&0xFF;
            switch (subCmd) {
                case 0x01: {
                    LocateStatusDto lastStatus = Optional.ofNullable((LocateStatusDto) TrackCacheUtil.get(TrackCacheUtil.KEY_LOCATE_STATUS)).orElse(LocateStatusDto.builder().build());
                    // 获取定位状态
                    byte locatedStatus = buffer.readByte();//1运行中/2定位成功/3定位失败/4避障触发/5跑出轨道末端/6跑出坞站
                    short lastId = buffer.readShortLE();//上一个完成定位点ID号 从 0 开始是正常的 0是起始点 -1是开机默认值
                    short nextId = buffer.readShortLE();//正要执行下一个定位点ID号
                    int speedConfig = buffer.readIntLE();//设置的运行速度（单位cm/s)
                    int speed = buffer.readIntLE();//当前运行速度（单位cm/s)
                    int mileage = buffer.readIntLE();
                    if (locationLogLimiter.tryAcquire(60) || lastStatus.getLocatedStatus()!=locatedStatus || lastStatus.getLastId()!=lastId || lastStatus.getNextId()!=nextId || lastStatus.getSpeedConfig()!=speedConfig ||lastStatus.getSpeed()!=speed || Math.abs(lastStatus.getMileage()-mileage)>100) {
                        log.info("[track]located status:{},lastId:{},nextId:{},speedConfig:{}cm/s,speed:{}cm/s,mileage:{}mm", locatedStatus, lastId, nextId, speedConfig, speed, mileage);
                    }
                    int mileageAdd = Math.abs(lastStatus.getMileage() - mileage);
                    if (mileageAdd>1 && mileage < 1500){
                        // 累计里程
                        TrackToolkit.totoMileage.getAndAdd(mileageAdd);
                    }
                    // 保存信息
                    LocateStatusDto currentStatus = LocateStatusDto.builder().locatedStatus(locatedStatus).lastId(lastId).nextId(nextId).speedConfig(speedConfig).speed(speed).mileage(mileage).build();
                    TrackCacheUtil.put(TrackCacheUtil.KEY_LOCATE_STATUS, currentStatus);
                    // 发布定位事件
                    SpringUtil.getApplicationContext().publishEvent(currentStatus);
                    break;
                }
                case 0x02:{
                    //获取语音对讲模式 0监听模式/1对讲模式
                    byte status = buffer.readByte();
                    log.info("[track] speaker status:{}",status);
                    break;
                }
                case 0x03:{
                    //获取从动轮里程计值
                    int mileage = buffer.readIntLE();
                    log.info("[track]driven wheel mileage:{}mm",mileage);
                    break;
                }
                case 0x04:{
                    //获取机器人运行模式 （0正常巡检模式/1标定模式 ）
                    byte mode = buffer.readByte();
                    if (TrackToolkit.runningMode!=mode) {
                        log.info("[track]running mode:{}", mode == (byte) 0 ? "正常巡检模式" : "标定模式");
                    }
                    TrackToolkit.runningMode = mode;
                    break;
                }
                case 0x05:{
                    //获取标定定位点信息（rfid标签）
                    short count = buffer.readShortLE();//总有效的定位点数量
                    byte isInArea = buffer.readByte();//是否在标定范围 0不在 1在
                    byte isInList = buffer.readByte();//是否在定位队列中 0不在 1在 即该点位是否已经标定过
                    short number = buffer.readShortLE();//当前定位点在队列中编号（已被编号从0开始，不存在队列中的为0xFFFF溢出为-1）
                    int mileage = buffer.readIntLE();//当前里程计值（单位mm）
                    byte[] rfidData = new byte[12];
                    buffer.readBytes(rfidData);
                    StringBuilder rfidBuf = new StringBuilder();
                    for (byte b : rfidData) {
                        // 将每个字节转换为两位的十六进制字符串
                        rfidBuf.append(String.format("%02X", b));
                    }
                    if (!Objects.equals(rfidBuf.toString(),"000000000000000000000000")) {
                        log.info("[track]rfidInfo current rfid{},mileage{},isInArea:{},isInList:{},number:{}; summary count:{}", rfidBuf, mileage, isInArea, isInList, number, count);
                    }
                    //保存信息
                    TrackCacheUtil.put(TrackCacheUtil.KEY_CALIBRATION_POINT_INFO, CalibrationPointDto.builder().count(count).isInArea(isInArea).isInList(isInList).number(number).mileage(mileage).rfid(rfidBuf.toString()).build());
                    break;
                }
                case 0x06:{
                    //获取机器人控制模式
                    int lastRemoteStatus = TrackToolkit.controlMode ==(byte) 2 ? 1 : 0;
                    byte mode = buffer.readByte();
                    String modeStr = "";
                    if (mode == (byte) 0){
                        modeStr = "无控制模式";
                    }else if (mode == (byte) 1){
                        modeStr = "上位机控制模式";
                    }else if (mode == (byte) 2){
                        modeStr = "遥控模式";
                    }
                    if (TrackToolkit.controlMode!=mode) {
                        log.info("[track]control mode:{}", modeStr);
                    }
                    TrackToolkit.controlMode = mode;
                    // 发布遥控事件
                    int remoteStatus = mode ==(byte) 2 ? 1 : 0;
                    if (lastRemoteStatus!=remoteStatus) {
                        SpringUtil.getApplicationContext().publishEvent(TrackRemoteModeTriggerEvent.builder().remoteTriggerStatus(remoteStatus).build());
                    }
                    break;
                }
                case 0x07:{
                    //获取RFID读取功率
                    byte power = buffer.readByte();
                    log.info("[track]rfid power:{}dbm", power);
                    //保存信息
                    TrackCacheUtil.put(TrackCacheUtil.KEY_RFID_READER_POWER,power);
                    break;
                }
                case 0x17:{
                    //获取RFID读取功率
                    int mileageCalibrationFactor = buffer.readIntLE();
                    log.info("[track]mileage calibration factor:{}", mileageCalibrationFactor);
                    //保存信息
                    TrackCacheUtil.put(TrackCacheUtil.KEY_MILEAGE_CALIBRATION_FACTOR,mileageCalibrationFactor);
                    break;
                }
                case 0x08:{
                    //获取当前所有站点信息
                    short count = buffer.readShortLE();//总有效的定位点数量
                    short number = buffer.readShortLE();//当前定位点在队列中编号0开始
                    int mileage = buffer.readIntLE();//里程计值（单位mm）
                    byte[] rfidData = new byte[12];
                    buffer.readBytes(rfidData);
                    StringBuilder rfidBuf = new StringBuilder();
                    for (byte b : rfidData) {
                        // 将每个字节转换为两位的十六进制字符串
                        rfidBuf.append(String.format("%02X", b));
                    }
                    String rfid = rfidBuf.toString();
                    byte isOriginal = buffer.readByte();//是否是起始点（0不是；1是）
                    // 会收到多条消息，组成数组或集合比较麻烦，暂时只打印
                    log.info("[track]total {} ,number:{},label:{},mileage:{},isOriginal:{}",count,number,rfid,mileage,isOriginal);
                    break;
                }
                case 0x09:{
                    //收到心跳包 回复
                    TrackToolkit.sendHeartbeat();
                    break;
                }
                case 0x0B:{
                    //获取传感外设告警信息
                    byte motorBlock = buffer.readByte();// 电机是否堵转（0无，1有）
                    byte arrivePoint = buffer.readByte();//是否到达定位点铁片位置（0无，1有）
                    String driver0Fault = String.format("%08X",buffer.readIntLE());//驱动器0故障值
                    String driver1Fault = String.format("%08X",buffer.readIntLE());//驱动器1故障值
                    log.info("[track]motorBlock:{},arrivePoint:{},driver0Fault:{},driver1Fault:{}",motorBlock,arrivePoint,driver0Fault,driver1Fault);
                    break;
                }
                case 0x20:{
                    //获取自检状态-故障码
                    int netStatus = buffer.readIntLE();//外设通信自检状态 BIT0-12：左驱动器/右驱动器/左接近开关/右接近开关/急停开关/防撞条/库仑计/语音播报/气体传感器/RFID/里程计/前超声波/后超声波
                    if (lastNetStatus!=netStatus) {
                        logStatus(netStatus, 0x0001, "通讯自检异常","左驱动器通信状态异常");
                        logStatus(netStatus, 0x0002, "通讯自检异常","右驱动器通信状态异常");
                        logStatus(netStatus, 0x0004,"通讯自检异常", "左接近开关通信状态异常");
                        logStatus(netStatus, 0x0008, "通讯自检异常","右接近开关通信状态异常");
                        logStatus(netStatus, 0x0010, "通讯自检异常","急停开关通信状态异常");
                        logStatus(netStatus, 0x0020, "通讯自检异常","防撞条通信状态异常");
                        logStatus(netStatus, 0x0040, "通讯自检异常","库仑计通信状态异常");
                        logStatus(netStatus, 0x0080, "通讯自检异常","语音播报通信状态异常");
                        logStatus(netStatus, 0x0100, "通讯自检异常","气体传感器通信状态异常");
                        logStatus(netStatus, 0x0200, "通讯自检异常","RFID通信状态异常");
                        TrackToolkit.rfidReadNetError = (netStatus & 0x0200) != 0;
                        logStatus(netStatus, 0x0400, "通讯自检异常","里程计通信状态异常");
                        logStatus(netStatus, 0x0800, "通讯自检异常","前超声波通信状态异常");
                        logStatus(netStatus, 0x1000, "通讯自检异常","后超声波通信状态异常");
                    }
                    lastNetStatus = netStatus;
                    int leftDriverErrorCode = buffer.readIntLE();
                    if (lastLeftDriverErrorCode!=leftDriverErrorCode) {
                        logStatus(leftDriverErrorCode, 0xFFFFFFFF, "驱动器故障","左驱动器故障码:" + String.format("%08x", leftDriverErrorCode));
                    }
                    lastLeftDriverErrorCode = leftDriverErrorCode;
                    int rightDriverErrorCode = buffer.readIntLE();
                    if (lastRightDriverErrorCode!=rightDriverErrorCode) {
                        logStatus(rightDriverErrorCode, 0xFFFFFFFF, "驱动器故障","右驱动器故障码:" + String.format("%08x", rightDriverErrorCode));
                    }
                    lastRightDriverErrorCode = rightDriverErrorCode;
                    int runningErrorCode1 = buffer.readIntLE();
                    if (lastRunningErrorCode1!=runningErrorCode1) {
                        logStatus(runningErrorCode1, 0x01, "停止状态触发","急停触发");
                        logStatus(runningErrorCode1, 0x02, "停止状态触发","防撞条触发");
                        logStatus(runningErrorCode1, 0x04, "停止状态触发","避障触发");
                        logStatus(runningErrorCode1, 0x08, "停止状态触发","轮子堵转");
                    }
                    lastRunningErrorCode1 = runningErrorCode1;
                    int runningErrorCode2 = buffer.readIntLE();//预留
                    byte ultrasonicTriggerStatus = buffer.readByte();
                    //超声波触发
                    if (lastUltrasonicTriggerStatus!=ultrasonicTriggerStatus) {
                        int frontUltrasonicTriggerStatus = (ultrasonicTriggerStatus & 0x01) == 0 ? 0 : 1;
                        int backUltrasonicTriggerStatus = (ultrasonicTriggerStatus & 0x02) == 0 ? 0 : 1 ;
                        String ultrasonicRealValue = Optional.ofNullable((String) TrackCacheUtil.get(TrackCacheUtil.KEY_ULTRASONIC_REALTIME_DATA)).orElse("");
                        //发布事件
                        SpringUtil.getApplicationContext().publishEvent(TrackUltrasonicTriggerEvent.builder()
                                    .backUltrasonicTriggerStatus(backUltrasonicTriggerStatus)
                                    .frontUltrasonicTriggerStatus(frontUltrasonicTriggerStatus)
                                    .realValue(ultrasonicRealValue).build());
                        log.info("ultrasonic trigger {},front:{},back:{}",ultrasonicTriggerStatus,frontUltrasonicTriggerStatus,backUltrasonicTriggerStatus);
                    }
                    lastUltrasonicTriggerStatus = ultrasonicTriggerStatus;
                    int speakerReqStatus = buffer.readByte() & 0xFF;
                    if (TrackToolkit.speakerReqStatus!=speakerReqStatus && speakerReqStatus==0) {
                        log.info("[track]speaker request!");
                    }
                    TrackToolkit.speakerReqStatus = speakerReqStatus == 0 ? 0 : 1;
                    // 无线充电器故障码
                    short chargerError = buffer.readShortLE();//
                    if (lastChargerError!=chargerError){
                        String chargerErrorStr = String.format("%02X", chargerError & 0xFFFF);
                        logStatus(chargerError,0xFFFF,"充电故障","故障码:0x"+chargerErrorStr);
                    }
                    lastChargerError = chargerError;
                    float electricBinTemperature = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP).floatValue();
                    float electricBinHumidity = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP).floatValue();
                    float batteryBinTemperature = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP).floatValue();
                    float batteryBinHumidity = BigDecimal.valueOf(buffer.readShortLE()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP).floatValue();
                    if (logLimiter.tryAcquire(60 * 5)) {
                        log.info("[track]electric bin t:{}℃ h:{},battery bin t:{}℃ h:{}", electricBinTemperature, electricBinHumidity, batteryBinTemperature, batteryBinHumidity);
                    }
                    break;
                }
                case 0x21:{
                    //获取充电桩状态（轨道式机器人-充电桩WIFI模块 需要建一个tcp-client去主动连接）
                    byte reachStatus = buffer.readByte();//机器人到站状态(接近开关）
                    byte cleanerStatus = buffer.readByte();//吹风机开关状态
                    byte chargerNetStatus = buffer.readByte();//无线充电器通信状态
                    short chargeStatus = buffer.readShortLE();//充电状态：00：未充电 01：充电中 02：异常
                    short chargeErrorCode = buffer.readShortLE();//00：无故障 01：充电过压； 02：充电过流 03：设备过温
                    float chargerVoltage = BigDecimal.valueOf(buffer.readIntLE()).divide(BigDecimal.valueOf(1000), 3, RoundingMode.HALF_UP).floatValue();
                    float chargerCurrent = BigDecimal.valueOf(buffer.readIntLE()).divide(BigDecimal.valueOf(1000), 3, RoundingMode.HALF_UP).floatValue();
                    log.info("[track][charger]reach:{},cleaner:{},net:{},chargeStatus:{},error code:{},voltage:{},current:{}",reachStatus,cleanerStatus,chargerNetStatus,chargeStatus,chargeErrorCode,chargerVoltage,chargerCurrent);
                    break;
                }
                case 0x23:{
                    //获取最大运行速度阈值
                    float maxPatrolSpeed = BigDecimal.valueOf(buffer.readIntLE()).divide(BigDecimal.valueOf(1000), 3, RoundingMode.HALF_UP).floatValue();
                    log.info("[track]max patrol speed:{} m/s",maxPatrolSpeed);
                    break;
                }
                case 0x24:{
                    //获取气体传感器使能状态
                    byte methaneEnable = buffer.readByte();
                    byte smokeEnable = buffer.readByte();
                    byte speakerEnable = buffer.readByte();
                    log.info("[track]current air enable:methane:{},smoke:{},speaker:{}",methaneEnable,smokeEnable,speakerEnable);
                    break;
                }
                default:
                    break;
            }
            TrackToolkit.release(message, true, true);
        } catch (Exception e) {
            log.error("[track]deal  {}{}-message error,{}",String.format("%02x",mainCmd),String.format("%02x",subCmd),e.getMessage());
        } finally {
            // 确保 ByteBuf 在使用完后释放，防止内存泄漏
            buffer.release();
        }
    }


    // 通用的日志记录方法
    private int logStatus(int netStatus, int mask,String title,String message) {
        int result = netStatus & mask;
        if ((result) != 0) {
            log.info("[track]{}:{}",title,message);
            //报警
            AddWarningDto addWarningDto = new AddWarningDto();
            addWarningDto.setTitle(title);
            addWarningDto.setContent(message);
            addWarningDto.setCode("");
            addWarningDto.setSuggestion("");
            addWarningDto.setLevel(0);
            SpringUtil.getApplicationContext().publishEvent(addWarningDto);
        }
        return result;
    }



}
